#include "mstp_types.h"
#include "mstp_proto.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

/*
ssize_t mstp_recv (mstp_t *socket, const void *buff, size_t len);
int mstp_close (mstp_t *socket);
*/

#undef PUBLIC
#define PUBLIC 

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
 *	FUNCTION PROTOTIPES						    			*			
 *----------------------------------------------------------------------------------------------*
 *      RETURN          FUNCION NAME             ARGUMENTS		       			*
 *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

PRIVATE chunk_cache_t *	bring_out_chunk		(chunk_cache_t**,u_int32_t);
PRIVATE int 	       	clean_send_buffer	(stream_t *);
PRIVATE int 	       	chunk_end         	(stream_t *);
PRIVATE int 	       	pick_up_chunk           (chunk_cache_t**,stream_t*);
PRIVATE u_int16_t	choice_chunk_sz		(size_t,int);
PRIVATE void 		put_chunk 		(stream_t *,const void *, u_int16_t, u_int32_t); 
PRIVATE ssize_t 	distribute_chunks	(mstp_t *,const void *,size_t);
PRIVATE ssize_t 	flush_all		(stream_queue_t *,size_t);
PRIVATE int 		stream_rdy		(stream_queue_t *,stream_queue_t *,stream_queue_t*,struct timeval *);
PRIVATE void 		enqueue_rdy_send        (stream_queue_t *,stream_t *);
PRIVATE void 		enqueue_rdy_recv	(stream_queue_t *,stream_t *);
PRIVATE void 		enqueue_	        (stream_queue_t *,stream_t *);
PRIVATE stream_t*	dequeue_		(stream_queue_t *);
PRIVATE stream_t*	dequeue_rdy_send	(stream_queue_t *);
PRIVATE stream_t*	dequeue_rdy_recv	(stream_queue_t *);
PRIVATE stream_t*	new_stream		(u_int32_t,u_int32_t);
PRIVATE int 		stream_connect		(stream_t *,struct sockaddr_in *);


PUBLIC int mstp_accept (mstp_t *mstp_socket, int streams)
{
    mstp_proto_t 	proto;
    struct sockaddr_in	rhost_addr;
    ssize_t  		recv_bytes;
    u_int8_t 		nr_streams;
    stream_t 		*stream;
    u_int32_t 		r_buff_len;
    u_int32_t 		s_buff_len;
    u_int32_t		buff_size;
    socklen_t		len_addr;
    int 		socket_tcp;
    int 		yes;
    
    if (streams > mstp_socket->max_streams)
	return -1;
	

    len_addr = sizeof(struct sockaddr_in);	
	
    recv_bytes = recvfrom(mstp_socket->udp_socket, 
		       (void *)&proto, 
		       sizeof(mstp_proto_t),
		       0, 
		       (struct sockaddr *)&rhost_addr, 
		       &len_addr);
		       
		       
    if (recv_bytes == -1 || recv_bytes < sizeof(mstp_proto_t))
	return -1;
    	
    if (proto.msg_type == CNT) {
	nr_streams = (proto.msg_body.cnt.streams > streams) ? streams : proto.msg_body.cnt.streams;
	proto.msg_type = ACK;
	proto.msg_body.ack.streams = nr_streams;
	inet_aton("23.21.207.252", &(mstp_socket->addr.sin_addr));
	proto.msg_body.ack.addr	   = mstp_socket->addr.sin_addr.s_addr;
	proto.msg_body.ack.port    = mstp_socket->addr.sin_port;
	
	
	inet_aton("10.96.186.27", &(mstp_socket->addr.sin_addr));
	socket_tcp = socket(PF_INET, SOCK_STREAM, 0);
	if (socket_tcp == -1)
	    return -1;

	yes = 1;	
	if (setsockopt(socket_tcp, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	    return -1;
	    
	if (bind(socket_tcp, (struct sockaddr *)&(mstp_socket->addr), sizeof(struct sockaddr_in)) == -1)
	    return -1;
	
	if (listen(socket_tcp, 5) == -1)
	    return -1;
	    	
	if (sendto(mstp_socket->udp_socket, &proto, sizeof(mstp_proto_t),0,(struct sockaddr *)&rhost_addr,sizeof(struct sockaddr_in) ) == -1)
	    return -1;    	
	    	
	r_buff_len = MAX_RECV_BUFF_LEN;
	s_buff_len = MAX_BUFF_LEN / nr_streams;
	
	mstp_socket->nr_streams = nr_streams;
	
	while ( nr_streams > 0 ) {
	
	    stream = new_stream(r_buff_len, s_buff_len);
	    if (stream != NULL) {
	
		stream->socket = accept(socket_tcp, (struct sockaddr *) 0,( socklen_t *) 0);
		buff_size = 1024*1024;
		setsockopt(stream->socket, SOL_SOCKET, SO_RCVBUF,  &buff_size, sizeof(u_int32_t));
		fcntl(stream->socket, F_SETFL, O_NONBLOCK);
		printf ("Socket: %d\n", stream->socket);
		if (stream->socket != -1) {
		    enqueue_(&(mstp_socket->streams), stream);
		    nr_streams--;
		}
		else {
		    printf ("%d\n", errno);
		    /*
		     * Fuga de memoria
		     */
		    perror("accept():");
		    return -1;
		}		    
	    }	
	    else {
		/*
		 * Fuga de memoria
		 */
		return -1;
	    }
	
	}
	close(socket_tcp);

    }
    else {
	return -1;
    }
    return 0;
}


PUBLIC int mstp_bind(mstp_t *socket, struct sockaddr_in *addr)
{
    memcpy(&(socket->addr), addr, sizeof(struct sockaddr_in));
    return bind(socket->udp_socket, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
}

PUBLIC int mstp_connect(mstp_t *mstp_socket, struct sockaddr_in *addr, struct timeval *timeout)
{
    struct sockaddr_in	host_addr;
    mstp_proto_t 	proto;
    stream_t	 	*stream;
    fd_set 		recvset;
    socklen_t		len_addr;
    u_int32_t		r_buff_len;
    u_int32_t		s_buff_len;
    u_int8_t  		nr_s;
    int 		ret_val;
    
    
    FD_ZERO(&recvset);
        
    proto.msg_type    	       = CNT;
    proto.msg_body.cnt.streams = 4;
    
    ret_val = sendto(mstp_socket->udp_socket, &proto, sizeof(mstp_proto_t), 0, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
    if ( ret_val == -1 ) 
	return -1;
	
    
    FD_SET(mstp_socket->udp_socket, &recvset);
    ret_val = select(mstp_socket->udp_socket+1,&recvset, NULL, NULL, timeout); 
    if ( ret_val == 0 ) {
	/*
	 * Timeout
	 */
    
	return -1;
    }
    if (FD_ISSET(mstp_socket->udp_socket, &recvset)) {
	len_addr = sizeof(struct sockaddr_in);
	ret_val = recvfrom(mstp_socket->udp_socket, &proto, sizeof(mstp_proto_t), 0,(struct sockaddr *) &host_addr,&len_addr);
    
	if ( ret_val == -1 )
	    return -1;

	printf ("Addr: %s\n", inet_ntoa(host_addr.sin_addr));
	
	if ((addr->sin_port == host_addr.sin_port) && (addr->sin_addr.s_addr == host_addr.sin_addr.s_addr )) {
	    /*
	     * Al mismo que le envie me esta respondiendo
	     */
	    if (proto.msg_type == ACK) {
	
		nr_s = proto.msg_body.ack.streams;

		mstp_socket->nr_streams = nr_s;

		host_addr.sin_port 		= proto.msg_body.ack.port;
		host_addr.sin_addr.s_addr 	= proto.msg_body.ack.addr;		
	    
		r_buff_len = MAX_RECV_BUFF_LEN;
		s_buff_len = MAX_BUFF_LEN / nr_s;
	    
		while (nr_s > 0) {
		
		    stream = new_stream(r_buff_len, s_buff_len);
		    if (stream == NULL)
			return -1;
				
		    if (stream_connect(stream,&host_addr)== -1)
			return -1;
		    
		    enqueue_(&(mstp_socket->streams), stream);    
	
		    nr_s--;	
		}
	    }
	    else {
		return -1;
	    }
	}
	else {
	    return -1;
	}
    }
    else {
	return -1;
    }
    return 0;
}


PRIVATE stream_t *new_stream(u_int32_t recv_buff_len, u_int32_t send_buff_len)
{
    stream_t *stream;
    stream = (stream_t *)calloc(1, sizeof(stream_t));
    
    if (stream != NULL) {
	stream->socket         = 0;
	stream->bytes_send     = 0;
	stream->bytes_recv     = 0;
	stream->nr_rdy_s_times = 0;
	stream->nr_rdy_r_times = 0;
	stream->bytes_in_queue = 0;
    	if (gettimeofday(&(stream->start_time), NULL)==-1)
    	    return NULL;
	stream->average_speed = 0;
	stream->p_nextready_send = NULL;
	stream->p_nextready_recv = NULL;
	stream->p_next		 = NULL;
    
	stream->send_buffer.buff = (u_int8_t *) calloc(send_buff_len, 1); 
        if (stream->send_buffer.buff == NULL)
	    return NULL;    
        stream->recv_buffer.buff = (u_int8_t *) calloc(recv_buff_len, 1);
	if (stream->recv_buffer.buff == NULL) {
	    free(stream->send_buffer.buff);
	    return NULL;
	}
	
	stream->send_buffer.buff_sz = stream->send_buffer.bytes_free = send_buff_len;
	stream->recv_buffer.buff_sz = stream->recv_buffer.bytes_free = recv_buff_len;
	stream->send_buffer.bytes_occ = 0;
	stream->recv_buffer.bytes_occ = 0;	
	stream->send_buffer.buff_free = &(stream->send_buffer.buff)[0];
	stream->recv_buffer.buff_free = &(stream->recv_buffer.buff)[0];
	
	return stream;
    }
    return NULL;
}


PRIVATE int stream_connect(stream_t *stream,struct sockaddr_in *addr)
{
    stream->socket = socket(PF_INET, SOCK_STREAM, 0);
    if (stream->socket == -1)
	return -1;

    printf ("Socket: %d\n", stream->socket);	
    return connect(stream->socket,(struct sockaddr *)addr, sizeof(struct sockaddr_in));	
}





PRIVATE void enqueue_(stream_queue_t *queue, stream_t *ptr)
{
    ptr->p_next = NULL;
    if (queue->tail == NULL && queue->head == NULL)
        queue->head = ptr;
    else
        queue->tail->p_next = ptr;	
    queue->tail = ptr;
}

PRIVATE stream_t *dequeue_(stream_queue_t *queue)
{
    struct stream *ptr;
    
    ptr = queue->head;
    if (ptr) {
	queue->head = ptr->p_next;
    }
    return ptr;
}

PRIVATE stream_t *dequeue_rdy_send(stream_queue_t *queue)
{
    struct stream *ptr;
    
    ptr = queue->head;
    if (ptr) {
	queue->head = ptr->p_nextready_send;
	ptr->p_nextready_send = NULL;
    }
    return ptr;
}
PRIVATE stream_t *dequeue_rdy_recv(stream_queue_t *queue)
{
    struct stream *ptr;
    
    ptr = queue->head;
    if (ptr) {
	queue->head = ptr->p_nextready_recv;
	ptr->p_nextready_recv = NULL;
    }
    return ptr;
}


PRIVATE void enqueue_rdy_send(stream_queue_t *queue, stream_t *ptr)
{
    ptr->p_nextready_send = NULL;
    if (queue->tail == NULL && queue->head == NULL)
        queue->head = ptr;
    else
        queue->tail->p_nextready_send = ptr;	
    queue->tail = ptr;
}


PRIVATE void enqueue_rdy_recv(stream_queue_t *queue, stream_t *ptr)
{
    ptr->p_nextready_recv = NULL;
    if (queue->tail == NULL && queue->head == NULL)
        queue->head = ptr;
    else
        queue->tail->p_nextready_recv = ptr;	
    queue->tail = ptr;
}


PRIVATE chunk_cache_t *bring_out_chunk (cache,chunk_nr)
chunk_cache_t **cache;
u_int32_t     chunk_nr;
{
    chunk_cache_t *tmp_cache = NULL;
    chunk_cache_t *ptr;
    chunk_cache_t *ret = NULL;
    
    ptr = *cache;
    while ( ptr != NULL ) {
	(*cache) = ptr->next;
	if (ptr->hdr.chunk_nr == chunk_nr) {
	    ret = ptr;
	    ret->next = NULL;
	}
	else {
	    ptr->next = tmp_cache;
	}
	ptr = *cache;    
    }
    *cache = tmp_cache;
    return ret;
}


PRIVATE int clean_send_buffer (stream)
stream_t *stream;
{
    if ( stream != NULL ) {
	if (stream->bytes_in_queue == 0) {
	    stream->send_buffer.bytes_occ  = 0;
	    stream->send_buffer.bytes_free = stream->send_buffer.buff_sz;
	    stream->send_buffer.buff_free  = &(stream->send_buffer.buff)[0];
	    memset(stream->send_buffer.buff, 0, stream->send_buffer.buff_sz);
	    return 0;
	}
	else {
	    return -1;
	}
    }
    else {
	return -1;
    }
}


PRIVATE int chunk_end(stream)
stream_t *stream;
{
    chunk_hdr_t *hdr;
    if (stream->recv_buffer.bytes_occ > CHUNK_HDR_SZ) {
	hdr = (chunk_hdr_t *) &(stream->recv_buffer.buff)[0];
	if (hdr->chunk_sz <= (stream->recv_buffer.bytes_occ - CHUNK_HDR_SZ))
	    return 1;
    }
    return 0;
}


PRIVATE int pick_up_chunk (cache,stream)
chunk_cache_t **cache;
stream_t      *stream;
{
    chunk_hdr_t   *hdr;
    chunk_cache_t *ptr;
    
    ptr = calloc(sizeof(chunk_cache_t),1);
    if (ptr) {
	memcpy(&(ptr->hdr),&(stream->recv_buffer.buff)[0], CHUNK_HDR_SZ);
	hdr = &(ptr->hdr);
	memcpy(ptr->data,&(stream->recv_buffer.buff)[CHUNK_HDR_SZ], hdr->chunk_sz);
	ptr->next = *cache;
	*cache = ptr;
	
	stream->recv_buffer.bytes_occ  -= (CHUNK_HDR_SZ + hdr->chunk_sz);
	stream->recv_buffer.bytes_free += (CHUNK_HDR_SZ + hdr->chunk_sz);
	stream->recv_buffer.buff_free  = (u_int8_t *) (&(stream->recv_buffer.buff_free)[0] - (u_int8_t*) (CHUNK_HDR_SZ + hdr->chunk_sz));
    
	return 0;
    }
    return -1;
}


PUBLIC int mstp_socket (mstp_socket)
mstp_t *mstp_socket;
{
    mstp_socket->io_buffer.buff = (u_int8_t *) calloc(1, MAX_BUFF_LEN);
    if ( mstp_socket->io_buffer.buff ) {
    
	mstp_socket->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
    
	if ( mstp_socket->udp_socket != -1 ) {
    
	    mstp_socket->io_buffer.bytes_occ  = 0;
	    mstp_socket->io_buffer.bytes_free = MAX_BUFF_LEN;
	    mstp_socket->io_buffer.buff_sz    = MAX_BUFF_LEN;
	    mstp_socket->io_buffer.buff_free  = &(mstp_socket->io_buffer.buff)[0];	
	
	    mstp_socket->chunk_seq_send = 0;
	    mstp_socket->chunk_seq_recv = 0;
	    mstp_socket->nr_streams     = 0;

	    mstp_socket->cache = NULL;
	    mstp_socket->streams.head = NULL;
    	    mstp_socket->streams.tail = NULL;
	    mstp_socket->max_streams  = DEFAULT_MAX_STREAMS;

	    memset(&(mstp_socket->addr), 0, sizeof(struct sockaddr_in));
	
	    return  0;
	}
	else {
	    return -1;
	}
    }
    else {
    
	return -1;
    }
}


PRIVATE u_int16_t choice_chunk_sz (len,nr_streams)
size_t len;
int    nr_streams;
{
    u_int32_t cz;
    int div = 0;
    
    do {
	div = div + nr_streams; 
	cz  = len / div;
    } while ( cz > MAX_CHUNK_SZ);

    return cz;
}



PRIVATE void put_chunk (stream,buffer,sz,seq)
stream_t   *stream;
const void *buffer;
u_int16_t   sz;
u_int32_t   seq;
{
    chunk_hdr_t chdr;

    chdr.chunk_sz = sz;
    chdr.chunk_nr = seq;

    memmove(stream->send_buffer.buff_free, &chdr, CHUNK_HDR_SZ);
    memmove(&(stream->send_buffer.buff_free)[CHUNK_HDR_SZ], buffer, sz);
    
    stream->bytes_in_queue 	   += (sz + CHUNK_HDR_SZ);
    stream->send_buffer.bytes_free -= (sz + CHUNK_HDR_SZ);
    stream->send_buffer.bytes_occ  += (sz + CHUNK_HDR_SZ);
    
    stream->send_buffer.buff_free   = &(stream->send_buffer.buff_free)[sz + CHUNK_HDR_SZ];
}


PUBLIC ssize_t mstp_send(mstp_t *socket, const void *buffer, size_t len)
{
    ssize_t total_len;
    
    if ( (total_len = distribute_chunks(socket,buffer,len)) == -1 )
	return (ssize_t) -1;
	
    return flush_all(&(socket->streams), total_len);    
}



PRIVATE ssize_t distribute_chunks (socket,buffer,len)
mstp_t     *socket;
const void *buffer;
size_t     len;
{
    ssize_t     total_len;
    u_int16_t   cz;
    u_int32_t   nr_chunks;
    stream_t    *ptr_stream;
    u_int8_t    *buff_ptr = (u_int8_t *) buffer;
    
    size_t mod;
    
    if ( len > ATOMIC_SEND )
	/*
	 * Trucate
	 */
	len = ATOMIC_SEND;
	
    
    cz = choice_chunk_sz(len,socket->nr_streams);
    nr_chunks = len / cz;
    mod       = len % cz;
    if ( mod )
	nr_chunks++;

    total_len = (nr_chunks * CHUNK_HDR_SZ) + len;

    while (nr_chunks > 0) {

	ptr_stream = dequeue_(&(socket->streams));

	if ( nr_chunks == 1 && mod ) {
	    if (ptr_stream != NULL ) {
		put_chunk(ptr_stream, buff_ptr, mod, socket->chunk_seq_send++);
		buff_ptr = &buff_ptr[mod + CHUNK_HDR_SZ]; 
	    }
	    else 
		return (ssize_t) -1;
	} 
	else {
	    if (ptr_stream != NULL) {
		put_chunk(ptr_stream, buff_ptr, cz, socket->chunk_seq_send++);	    	    
    	        buff_ptr = &buff_ptr[cz + CHUNK_HDR_SZ];
	    }
	    else
		return (ssize_t) -1;
	}
	enqueue_(&(socket->streams), ptr_stream);
	nr_chunks--;
    }
    
    return total_len;
}
    

PUBLIC ssize_t mstp_recv(stream_queue_t *streams)
{
    stream_queue_t recvq;
    stream_t *ptr;
    ssize_t last_recv = 0;
    char buffer[1024*1024];
    recvq.head = NULL;
    recvq.tail = NULL;
    struct timeval ahora, despues;
    
    gettimeofday(&ahora, NULL);
    while ( 1 ) {
	if (stream_rdy(streams, &recvq, NULL, NULL)==-1)
	    return -1;
	ptr = dequeue_rdy_recv(&recvq);
	while ( ptr != NULL ) {
	    last_recv += recv(ptr->socket, buffer, 8024*1024, MSG_DONTWAIT);
	    ptr = dequeue_rdy_recv(&recvq);
	}
	gettimeofday(&despues, NULL);
	if (last_recv == 0)
	    break;
	printf ("Last: %.2f\n", ((float)last_recv / (despues.tv_sec - ahora.tv_sec))/(1024*1024));
    }

}


PRIVATE ssize_t flush_all(streams,len)
stream_queue_t *streams;
size_t         len;
{
    stream_queue_t sendq;
    stream_t *ptr;
    ssize_t  last_send;
    size_t   bytes_sent = 0;
    
    u_int32_t total, wsend;
    u_int8_t  *msg_ptr;
        
    sendq.head = NULL;
    sendq.tail = NULL;    

    /* ToDo: Calcular si len == a la suma de todos los wsend */

        
    while ( bytes_sent < len ) {
    
	if (stream_rdy(streams, NULL, &sendq, NULL) == -1)
	    return (ssize_t) -1;
	
	ptr = dequeue_rdy_send(&sendq);
	    
	while (ptr != NULL && bytes_sent < len ) {
	    /*
	     * Ancillary Calc
	     */
	    total   = ptr->send_buffer.bytes_occ;
	    wsend   = ptr->bytes_in_queue;
	    msg_ptr = &(ptr->send_buffer.buff)[total-wsend]; 
	    if (wsend > 0) {
		last_send = send(ptr->socket, msg_ptr, wsend, MSG_DONTWAIT);
		
		if (last_send > 0) {
		    bytes_sent          = bytes_sent          + last_send;
		    ptr->bytes_in_queue = ptr->bytes_in_queue - last_send;
		    ptr->bytes_send     = ptr->bytes_send     + last_send;
		}			     
		else {
		
		    if ( last_send == -1 ) {
			/*
			 * Evaluar errno
			 */    
			return -1;
		    } 
		}    
		/*
		 * Que hacemos ??
		 */
	    }
	    
	    ptr = dequeue_rdy_send(&sendq);
	} 
    }
    if ( bytes_sent == len ) {
	ptr = streams->head;
	while ( ptr != NULL ) {
	    if (clean_send_buffer(ptr) != 0) {
		return (ssize_t) -1;
	    }
	    ptr = ptr->p_next;
	}
    }
    else {
	return (ssize_t) -1;
    }
    return bytes_sent;
}    
    
PRIVATE int stream_rdy(stream_queue, recv, send, timeout)
stream_queue_t *stream_queue;
stream_queue_t *recv;
stream_queue_t *send;
struct timeval  *timeout;
{
    int retval = 0;
    int maxfd  = -1;
    int n;
    stream_t *ptr = NULL;
    fd_set recvset;
    fd_set sendset;
    fd_set *recvts;
    fd_set *sendts;
    FD_ZERO(&recvset);
    FD_ZERO(&sendset);
    if (stream_queue != NULL) {
	ptr = stream_queue->head;
	while ( ptr != NULL ) {
	    maxfd = (ptr->socket > maxfd) ? ptr->socket : maxfd;
	    FD_SET(ptr->socket, &recvset);
	    FD_SET(ptr->socket, &sendset);
	    ptr = ptr->p_next;
	}
	recvts = (recv != NULL) ? &recvset : NULL;
    	sendts = (send != NULL) ? &sendset : NULL;
	/*
	 * ToDo: This section needs to be ATOMIC (sigprocmask)
	 */ 
	n = select(maxfd+1, recvts, sendts, NULL, timeout);
	if (n == -1)
	    retval = -1;
	else {
	    if ( n > 0 && recv != NULL ) {
		ptr = stream_queue->head;
		recv->head = NULL;
		recv->tail = NULL;
		while ( ptr != NULL ) {
		    if (FD_ISSET(ptr->socket, recvts)) {
			n--;
			retval++;
			ptr->nr_rdy_r_times++;
			enqueue_rdy_recv(recv, ptr);
		    }
		    ptr = ptr->p_next;
		    if (n == 0)
			break;
		}
	    }
	    if ( n > 0 && send != NULL ) {
		ptr = stream_queue->head;
		send->head = NULL;
		send->tail = NULL;
		while ( ptr != NULL ) {
		    if (FD_ISSET(ptr->socket, sendts)) {
			n--;
			retval++;
			ptr->nr_rdy_s_times++;
			enqueue_rdy_send(send, ptr);
		    }
		    ptr = ptr->p_next;
		    if ( n == 0 )
			break;
		}  
	    }
	} /* else */
    } /* if (stream != NULL) */
    return retval;
}    
